home *** CD-ROM | disk | FTP | other *** search
- Programming NOTE:
-
- For those of you writing client programs for the internet. If you have
- noticed, when you are running a shell command that attaches to a host to
- get some information, you can abort a "stuck" request by simply typing
- <CTRL><c>. This is because the shell senses this sequence and sends a
- software interrupt to the process. Most, if not all, commands provided by
- current TCP stacks (AmiTCP, Termite TCP, ...) use a bsd standard which is
- that software interrupt signals will abort the current command.
-
- It would be nice if those of you programming GUI programs will add a way
- to simulate this so that commands, such as 'connect()', don't get "stuck"
- indefinately. What follows is a suggestion which works well. Feel free to
- use it, or a modification of it, in your code. I am providing this suggestion
- here because I am tired of running programs that sit and wait indefinately
- for some response, sometimes causing me to forceibly hang up the connection
- and reboot just to terminate the command.
-
- This "pseudo-code" is intended mostly as information. There is plenty of
- room for variation. However, specifically in regard to 'bsd' function calls,
- this technique can be used to add a timeout ability to any 'bsd' functions
- which do not have a timeout built in, such as 'connect()'. You could also
- use it to enhance the timeout for any function where the supplied timeout
- does not work quite as expected or desired.
-
- So, without further adieu, here it is...
-
- #include <signal.h>
- #include <dos.h>
- #include <dos/dos.h>
- #include <proto/exec.h>
-
- int Signals = 0;
- struct MsgPort *TimeOutPort = NULL;
-
- /* handler for trapping SIGINT
- * NOTE: interrupt handlers should be kept
- * as simple as possible!! YOU HAVE BEEN WARNED!!!
- */
- int SigInt( int Sigs ) {
- Signals |= Sigs;
- signal( SIGINT, SigInt );
- }
-
- /* Handler for trapping software exceptions.
- * IMPORTANT: This MUST be defined using this calling convention!!
- * This is because this is how the OS calls it when the exception happens.
- * (NOTE: declaring it 'static' is optional however)
- */
- __aligned __regargs static ULONG ExceptHand( ULONG Sigs, ULONG LocalData ) {
- struct Task *pTask = NULL;
-
- /* disable exception(s) specified in 'Sigs' so
- * they won't trigger additional exceptions while
- * you are processing this one.
- */
- SetExcept( 0, Sigs );
-
- /* signal a SIGINT to the task to abort the
- * socket command (i.e. 'connect()') or other process
- * NOTE: DO NOT use the standard 'C' function 'raise()'
- * to trigger the SIGINT as this causes the SIGINT to be
- * processed NOW rather than when you return from this exception
- * handler. As a result, by the time you return from this handler,
- * the signal has already been processed, and the signal
- * reset. As a result, the command/process you are trying to
- * interrupt never sees the signal and is never interrupted!!!
- * Since 'C' does not check for a SIGINT until you are performing
- * I/O, and since bsd function calls are I/O commands, then by
- * using the exec function 'Signal()' to set the SIGINT, then it
- * will check upon resuming the bsd function call which was
- * interrupted, thus aborting the command.
- */
- pTask = FindTask( NULL ); /* find "my" task info */
- if ( pTask ) {
- /* set the following signals:
- * SIGBREAKF_CTRL_C SIGINT
- * Sigs re-set the signal that triggered the exception
- * signal 8 it occasionally froze unless this was set
- */
- Signal( pTask, SIGBREAKF_CTRL_C | Sigs | 1<<8 ); /* signal me */
- }
- return Sigs;
- }
-
- /* set a timeout with an exception */
- void open_timer( int TimeOutSecs ) {
- if ( !TimeOutPort ) {
- /* open port to timer, and open timer device... */
- TimeOutPort = OpenPort( ... );
- :
- OpenDevice( ... );
- :
- :
- /* use the following to establish that messages
- * received at port will cause exceptions.
- */
- if ( TimeOutPort ) {
- SetExcept( 1 << TimeOutPort->mp_SigBit, 1 << TimeOutPort->mp_SigBit );
- }
- /* now, send a timeout request of 'TimeOutSecs'
- * seconds to the timer device.
- */
- }
- }
-
- /* disable port's exception and close timer and port */
- void close_timer( void ) {
- if ( TimeOutPort ) {
- /* disable exception so any pending messages
- * won't generate exceptions
- */
- SetExcept( 0, 1 << TimeOutPort->mp_SigBit );
- :
- :
- /* perform normal cleanup, such as aborting any
- * remaining pending timeout events and empty
- * the port. then close the timer device and the port.
- */
- CloseDevice( ... );
- :
- ClosePort( ... );
- }
- TimeOutPort = NULL;
- if ( Signals & SIGINT ) {
- /* any signal code to be executed by the SIGINT
- * (could be additional cleanup,...)
- */
- :
- :
- Signals &= ~SIGINT;
- }
- }
-
- your_main_loop( void ) {
- open_timer( 10 ); /* set a 10 second timeout */
- connect(); /* bsd socket function which we are trying to timeout */
- close_timer(); /* abort timeout if connect succeeded, or cleanup */
-
- for( .... ) {
- :
- :
- open_timer( 10 ); /* set a 10 second timeout */
- another_bsd_function_needing_timeout();
- close_timer(); /* abort timeout if connect succeeded, or cleanup */
- :
- :
- open_timer( 10 ); /* set a 10 second timeout */
- another_bsd_function_needing_timeout();
- close_timer(); /* abort timeout if connect succeeded, or cleanup */
- :
- :
- }
- }
-
- void open_socket( void ) {
- /* build host address, get access protocol, and
- * open the socket
- */
- socket( ... );
- }
-
- void close_socket( void ) {
- /* close the socket */
- ShutDown( ... );
- CloseSocket( ... );
- }
-
- int main( void ) {
- struct Task *pTask = FindTask( NULL );
- :
- :
- /* establish pointer to exception handler */
- if ( pTask ) {
- pTask->tc_ExceptCode = &ExceptHand;
- pTask->tc_ExceptData = NULL;
- }
- /* establish pointer to SIGINT handler
- * DO NOT use SIG_IGN or SIG_DFL instead of
- * 'SigInt'. using SIG_IGN will ignore signal
- * which nullifies all work being done. SIG_DFL
- * establishes default response to a SIGINT which
- * is to abort current process and exit from the
- * program, possibly leaving socket open and other
- * resources still allocated!!!
- */
- signal( SIGINT, SigInt );
- :
- :
- open_socket();
-
- /* process */
- your_main_loop();
-
- close_socket();
-
- return 0;
- }
-
- Explanation:
-
- This (pseudo) code:
- 1) establishes a timeout to wrap around any command needing one
- 2) sets a task exception to be generated when the timeout message arrives
- at the timer's message port
- 3) sets up an exception handler which is called when the exception is
- triggered. this handler, in turn, triggers a normal software interrupt
- SIGINT signal. upon return from the exception handler, the SIGINT
- handling is executed, including calling your handler and also
- aborting the current I/O command (i.e. connect())
- 4) establishes a handler to handle the SIGINT when triggered so your
- program does not abort and exit when it comes in
-
- Since the standard for 'bsd' function calls is to abort when a software
- interrupt is received by the program, then this should work for pretty
- much all 'bsd' socket calls, as well as for any functions which perform some
- kind of I/O (i.e. fprintf()).
-
- While it is true that message ports can be set to cause a soft interrupt
- when messages arrive, it looks very trickey to get this to work correctly.
- From the information I have (which is very scarce), it looks like you have
- to set up a signal handler, written in assembler, and installed in the OS
- signal handler list, which seems needlessly complicated for what I was
- trying to do. This method is very simple, in comparison, and requires no
- assembler code to get it to work, which makes it much easier to maintain.
-
- This method can also be tailored to a number of other situations. For example,
- if you find that you need to continually check for the arrival of messages
- during a processing loop, this method can be used to elimanate this need.
- Let the operating system "tell" you when the message is there by triggering
- an exception. This allows you to spend full concentration on whatever you
- are needing to do in your loop, and defers the message checking to the OS!!
- If desired, you could add code to the exception handler or your SIGINT handler
- to terminate the loop by setting the correct condition to terminate the
- processing loop eliminating the need to check for a SIGINT in the loop as well.
-
- --------------------
-
- A word on Exceptions:
-
- When you set an exception to be triggered upon receipt of a particular
- task signal, the corresponding task signal is reset to prevent the exception
- from being tripped again. As a result, if you call 'Wait()' or 'WaitPort()'
- after the exception has been triggered, and more messages exist at the port,
- the 'Wait' commands will NOT immediately return because the signal bit
- has been reset by the exception. You have 2 options at this point:
-
- 1) use Signal() to set the signal bit again so that the next 'Wait' call
- will not block.
-
- 2) process all remaining messages at the port before moving on. Do this by
- using 'GetMsg()/ReplyMsg()' until 'GetMsg()' returns NULL.
-
- Option 1 should ONLY be used while the port's exception is disabled.
- Otherwise, setting the signal bit will trigger the exception again reseting
- the bit again (nice huh??) and trigger another SIGINT.
-
- Option 2 should be used whenever possible. Then when all messages have been
- processed, it is safe to re-enable the exception for the port again and let
- message arrivals set the signal bit.
-
- Another WARNING: try not to use other kinds of I/O commands within the
- exception handler. If you do, use them BEFORE calling 'Signal()' to set the
- signal. The reason is because, as stated earlier, checks for SIGINT are made
- during I/O calls. 'printf()' and commands like that are I/O commands because
- they "output" to the screen. The check for, and processing of, the SIGINT will
- most likely occur DURING this call, which means, by the time you return
- from your exception handler, the SIGINT has already been processed, thus
- putting you back where you started... stuck on some command.
-